package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.zzyl.constant.SuperConstant;
import com.zzyl.dto.ResourceDto;
import com.zzyl.entity.Resource;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.ResourceMapper;
import com.zzyl.mapper.RoleResourceMapper;
import com.zzyl.service.ResourceService;
import com.zzyl.utils.NoProcessing;
import com.zzyl.utils.ThreadLocalUtil;
import com.zzyl.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Transactional
public class ResourceServiceImpl implements ResourceService {

    @Autowired
    private ResourceMapper resourceMapper;
    @Autowired
    private RoleResourceMapper roleResourceMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 根据用户id查询对应的资源数据
     *
     * @return
     */
    @Override
    public List<MenuVo> getMyMenus() {
        Long userId = ThreadLocalUtil.get();
        //查询数据
        List<MenuVo> menuVoList = resourceMapper.selectMenuByUserId(userId);
        //构建 parentNo -> List<MenuVo> 的映射
        Map<String, List<MenuVo>> parentMap = menuVoList.stream().collect(Collectors.groupingBy(MenuVo::getParentResourceNo));

        //递归构建我的菜单信息
        buildTree(menuVoList, parentMap);
        return parentMap.get(SuperConstant.ROOT_RESOURCE_PARENT_ID);
    }

    /**
     * 递归构建我的菜单信息（已提供）
     */
    private void buildTree(List<MenuVo> menuVoList, Map<String, List<MenuVo>> parentMap) {
        menuVoList.forEach(menuVo -> {
            //补全数据
            menuVo.setMeta(MenuMetaVo.builder().title(menuVo.getName()).build());
            menuVo.setRedirect("/" + menuVo.getName());
            //根据当前资源编号查询子资源
            List<MenuVo> childrenList = parentMap.get(menuVo.getResourceNo());
            if (CollUtil.isNotEmpty(childrenList)) {
                buildTree(childrenList, parentMap);
                menuVo.setChildren(childrenList);
            }
        });
    }

    /**
     * 资源列表
     *
     * @param dto
     * @return
     */
    @Override
    public List<ResourceVo> listResource(ResourceDto dto) {

        List<Resource> resourceList = resourceMapper.selectList(dto);
        return BeanUtil.copyToList(resourceList, ResourceVo.class);
    }

    /**
     * 资源树形结构
     *
     * @return
     */
    @Override
    @Cacheable(cacheNames = "resource", key = "#root.methodName")
    public TreeVo resourceTreeVo() {
        ResourceDto resourceDto = ResourceDto.builder()
                .resourceType("m")//m菜单 r按钮
                .build();
        //1. 查询所有的资源列表
        List<Resource> resourceList = resourceMapper.selectList(resourceDto);

        //2. 提取资源列表中的根节点
        Resource rootResource = CollUtil.findOneByField(resourceList, "resourceNo", SuperConstant.ROOT_RESOURCE_PARENT_ID);

        //3. 根据根节点、资源列表构建一颗树 TreeItemVo
        TreeItemVo treeItemVo = recursionBuilderTreeItemVo(rootResource, resourceList);

        //4. 对树包装一次
        return TreeVo.builder()
                .items(List.of(treeItemVo))
                .build();
    }


    private TreeItemVo recursionBuilderTreeItemVo(Resource rootResource, List<Resource> resourceList) {
        //创建根节点信息
        TreeItemVo treeItemVo = TreeItemVo.builder()
                .id(rootResource.getResourceNo()) //资源编号
                .label(rootResource.getResourceName()) //资源名称
                //.children() //子节点信息
                .build();

        //寻找根节点的子节点信息
        Collection<Resource> childrenList = CollUtil.filterNew(resourceList, resource -> resource.getParentResourceNo().equals(rootResource.getResourceNo()));

        //将  Collection<Resource>  转成  Collection<TreeItemVo>
        List<TreeItemVo> children = childrenList.stream()
                .map(resource -> recursionBuilderTreeItemVo(resource, resourceList))
                .collect(Collectors.toList());

        //补全根节点 的 子节点信息
        treeItemVo.setChildren(children);

        //返回
        return treeItemVo;
    }

    /**
     * 添加资源
     *
     * @param dto
     */
    @Override
    public void addResource(ResourceDto dto) {
        //将dto转成pojo
        Resource resource = BeanUtil.toBean(dto, Resource.class);

        //获取资源的父节点对象
        Resource parentResource = resourceMapper.selectByResourceNo(dto.getParentResourceNo());

        //创建资源编号，手动设置资源编号
        String resourceNo = createResourceNo(resource, parentResource);
        resource.setResourceNo(resourceNo);

        //添加到数据库
        resourceMapper.insert(resource);
    }


    /**
     * 创建资源的编号（已提供）
     * 创建资源的编号（已提供）
     * 创建资源的编号（已提供）
     */
    private String createResourceNo(Resource resource, Resource parentResource) {
        //判断是否是按钮，如果是按钮，则不限制层级
        if (!StrUtil.equals(resource.getResourceType(), "r")) {
            if (NoProcessing.processString(parentResource.getResourceNo()).length() / 3 >= 5) {
                //判断资源编号是否大于三级
                //100 001 000 000 000
                //100 001 001 000 000
                //100 001 001 001 000
                //100 001 001 001 001 001
                throw new BaseException(BasicEnum.RESOURCE_DEPTH_UPPER_LIMIT);
            }
        }

        //根据父资源编号查询子资源
        ResourceDto dto = ResourceDto.builder().parentResourceNo(parentResource.getResourceNo()).build();
        List<Resource> resources = resourceMapper.selectList(dto);

        if (ObjectUtil.isEmpty(resources)) {
            //无下属节点，创建新的节点编号  100 001 001 001 000---> 100 001 001 001 001
            return NoProcessing.createNo(parentResource.getResourceNo(), false);
        } else {
            //有下属节点，在已有的节点上追加，先获取已有节点的最大值--100001003000000
            Long maxNo = resources.stream()
                    .map(r -> Long.valueOf(r.getResourceNo()))
                    .max(Comparator.comparing(i -> i))
                    .get();
            //100 001 001 007 000 ->  100 001 001 008 000
            return NoProcessing.createNo(String.valueOf(maxNo), true);
        }
    }

    /**
     * 修改资源
     *
     * @param resourceDto
     */
    @Override
    public void updateResource(ResourceDto resourceDto) {
        resourceDto.setParentResourceNo(null); //禁止修改父节点
        //转换
        Resource resource = BeanUtil.toBean(resourceDto, Resource.class);
        resourceMapper.updateByPrimaryKeySelective(resource);
    }

    /**
     * 启用禁用
     *
     * @param resourceDto
     * @return
     */
    @Override
    public void isEnable(ResourceDto resourceDto) {
        //启用菜单
        if (StrUtil.equals(resourceDto.getDataState(), "0")) { //0:启用，1:禁用
            //判断父级菜单是否是禁用，如果是禁用，不允许启用
            String parentResourceNo = resourceDto.getParentResourceNo();
            Resource parentResource = resourceMapper.selectByResourceNo(parentResourceNo);
            if (parentResource != null && parentResource.getDataState().equals("1")) {//0:启用，1:禁用
                throw new BaseException(BasicEnum.PARENT_MENU_DISABLE);
            }
        }

        //判断是否有子菜单，如果有子菜单，则一起启用或禁用
        // NoProcessing.processString(resourceNo);     101202000000 -> 101202
        String resourceNo = resourceDto.getResourceNo();
        //根据资源编号修改状态
        resourceMapper.updateByResourceNo(resourceNo, resourceDto.getDataState());

        //修改资源的所有子资源状态
        String res = NoProcessing.processString(resourceNo);
        resourceMapper.updateByParentResourceNo(res, resourceDto.getDataState());
    }

    /**
     * 删除菜单
     *
     * @param resourceNo
     */
    @Override
    public void deleteByResourceNo(String resourceNo) {
        //判断是否有子菜单，则不能删除
        // select * from resource where parent_resource_no = ?
        int result = resourceMapper.hasChildByMenuId(resourceNo);
        if (result > 0) throw new RuntimeException("存在子菜单,不允许删除");

        //如果当前菜单被角色引用，则不能删除
        result = roleResourceMapper.checkMenuExistRole(resourceNo);
        if (result > 0) throw new RuntimeException("菜单已分配,不允许删除");

        //菜单启用状态,不能删除
        Resource resource = resourceMapper.selectByResourceNo(resourceNo);
        if (resource == null || resource.getDataState().equals("0")) {//0:启用，1:禁用
            throw new RuntimeException("菜单启用状态,不能删除");
        }

        resourceMapper.deleteByResourceNo(resourceNo);
    }
}



